/*-------------------------------------------------------------------------
 *
 * libpq-events.c
 *	  用于支持 libpq “events” API 的函数
 *
 * Portions Copyright (c) 1996-2022, PostgreSQL Global Development Group
 * Portions Copyright (c) 1994, Regents of the University of California
 *
 *
 * IDENTIFICATION
 *	  src/interfaces/libpq/libpq-events.c
 *
 *-------------------------------------------------------------------------
 */
#include "postgres_fe.h"

#include "libpq-fe.h"
#include "libpq-int.h"


/*
 * 注册一个事件处理程序与给定的 PGconn。
 *
 * 同一个处理程序不能在一个 PGconn 中注册多于一次。这个
 * 限制是必要的，因为我们使用处理程序地址来识别
 * 事件，例如用于 PQinstanceData()。
 *
 * name 参数用于错误消息中以帮助调试。
 * 必须提供一个名称，但不需要唯一。字符串会被
 * 复制，因此传递的值不需要长时间存在。
 *
 * passThrough 参数是一个特定于应用程序的指针，如果不需要，
 * 可以设置为 NULL。它会在每次调用事件处理程序时被传递，
 * libpq 不会对其进行其他操作。
 *
 * 如果成功，函数返回非零值。如果函数失败，
 * 则返回零。
 */
int PQregisterEventProc(PGconn *fc_conn, PGEventProc fc_proc,
					const char *fc_name, void *fc_passThrough)
{
	int			fc_i;
	PGEventRegister fc_regevt;

	if (!fc_proc || !fc_conn || !fc_name || !*fc_name)
		return false;			/* 参数错误 */

	for (fc_i = 0; fc_i < fc_conn->nEvents; fc_i++)
	{
		if (fc_conn->events[fc_i].proc == fc_proc)
			return false;		/* 已注册 */
	}

	if (fc_conn->nEvents >= fc_conn->eventArraySize)
	{
		PGEvent    *fc_e;
		int			fc_newSize;

		fc_newSize = fc_conn->eventArraySize ? fc_conn->eventArraySize * 2 : 8;
		if (fc_conn->events)
			fc_e = (PGEvent *) realloc(fc_conn->events, fc_newSize * sizeof(PGEvent));
		else
			fc_e = (PGEvent *) malloc(fc_newSize * sizeof(PGEvent));

		if (!fc_e)
			return false;

		fc_conn->eventArraySize = fc_newSize;
		fc_conn->events = fc_e;
	}

	fc_conn->events[fc_conn->nEvents].proc = fc_proc;
	fc_conn->events[fc_conn->nEvents].name = strdup(fc_name);
	if (!fc_conn->events[fc_conn->nEvents].name)
		return false;
	fc_conn->events[fc_conn->nEvents].passThrough = fc_passThrough;
	fc_conn->events[fc_conn->nEvents].data = NULL;
	fc_conn->events[fc_conn->nEvents].resultInitialized = false;
	fc_conn->nEvents++;

	fc_regevt.conn = fc_conn;
	if (!fc_proc(PGEVT_REGISTER, &fc_regevt, fc_passThrough))
	{
		fc_conn->nEvents--;
		free(fc_conn->events[fc_conn->nEvents].name);
		return false;
	}

	return true;
}

/*
 * 为 PGconn 中的一个事件设置一些 "实例数据"。
 * 成功时返回非零值，失败时返回零。
 */
int PQsetInstanceData(PGconn *fc_conn, PGEventProc fc_proc, void *fc_data)
{
	int			fc_i;

	if (!fc_conn || !fc_proc)
		return false;

	for (fc_i = 0; fc_i < fc_conn->nEvents; fc_i++)
	{
		if (fc_conn->events[fc_i].proc == fc_proc)
		{
			fc_conn->events[fc_i].data = fc_data;
			return true;
		}
	}

	return false;
}

/*
 * 获取事件的 "实例数据"，如果有的话。
 */
void * PQinstanceData(const PGconn *fc_conn, PGEventProc fc_proc)
{
	int			fc_i;

	if (!fc_conn || !fc_proc)
		return NULL;

	for (fc_i = 0; fc_i < fc_conn->nEvents; fc_i++)
	{
		if (fc_conn->events[fc_i].proc == fc_proc)
			return fc_conn->events[fc_i].data;
	}

	return NULL;
}

/*
 * 为 PGresult 中的一个事件设置一些 "实例数据"。
 * 成功时返回非零值，失败时返回零。
 */
int PQresultSetInstanceData(PGresult *fc_result, PGEventProc fc_proc, void *fc_data)
{
	int			fc_i;

	if (!fc_result || !fc_proc)
		return false;

	for (fc_i = 0; fc_i < fc_result->nEvents; fc_i++)
	{
		if (fc_result->events[fc_i].proc == fc_proc)
		{
			fc_result->events[fc_i].data = fc_data;
			return true;
		}
	}

	return false;
}

/*
 * 获取事件的 "实例数据"，如果有的话。
 */
void * PQresultInstanceData(const PGresult *fc_result, PGEventProc fc_proc)
{
	int			fc_i;

	if (!fc_result || !fc_proc)
		return NULL;

	for (fc_i = 0; fc_i < fc_result->nEvents; fc_i++)
		if (fc_result->events[fc_i].proc == fc_proc)
			return fc_result->events[fc_i].data;

	return NULL;
}

/*
 * 为应用程序创建的 PGresult 触发 RESULTCREATE 事件。
 *
 * 如果事件处理程序不使用，则 conn 参数可以为 NULL。
 */
int PQfireResultCreateEvents(PGconn *fc_conn, PGresult *fc_res)
{
	int			fc_result = true;
	int			fc_i;

	if (!fc_res)
		return false;

	for (fc_i = 0; fc_i < fc_res->nEvents; fc_i++)
	{
		/* 事件可能已经被触发，如果是这样就不要重复它 */
		if (!fc_res->events[fc_i].resultInitialized)
		{
			PGEventResultCreate fc_evt;

			fc_evt.conn = fc_conn;
			fc_evt.result = fc_res;
			if (fc_res->events[fc_i].proc(PGEVT_RESULTCREATE, &fc_evt,
									fc_res->events[fc_i].passThrough))
				fc_res->events[fc_i].resultInitialized = true;
			else
				fc_result = false;
		}
	}

	return fc_result;
}
